/*
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
* gazelle is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*     http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <stdint.h>
#include <securec.h>
#include <sys/socket.h>

#include <arch/sys_arch.h>

#include "common/dpdk_common.h"
#include "common/gazelle_reg_msg.h"
#include "lstack_protocol_stack.h"
#include "lstack_cfg.h"
#include "lstack_log.h"
#include "lstack_control_plane.h"
#include "lstack_rule.h"

static struct vdev_rule_reg_ops g_vdev_ltran_rule_ops;
static int vdev_ltran_reg_xmit(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    struct protocol_stack *stack = get_protocol_stack();

    int32_t ret;
    uint32_t sent_pkts = 0;
    void *free_buf[VDEV_REG_QUEUE_SZ];
    struct reg_ring_msg *tmp_buf = NULL;
    const uint32_t tbegin = sys_now();

    gz_addr_t src_ip = qtuple->src_ip;

    if (type == REG_RING_TCP_LISTEN || type == REG_RING_TCP_LISTEN_CLOSE) {
        if (!match_host_addr((ip_addr_t *)&src_ip)) {
            LSTACK_LOG(INFO, LSTACK, "lstack ip not match in conf.\n");
            return 0;
        }
    }

    uint32_t reg_index = stack->reg_head++ & DEFAULT_RING_MASK;
    do {
        (void)gazelle_ring_sc_dequeue(stack->reg_ring, free_buf, VDEV_REG_QUEUE_SZ);

        if (gazelle_ring_free_count(stack->reg_ring) == 0) {
            continue;
        }

        tmp_buf = &stack->reg_buf[reg_index];
        tmp_buf->type = type;
        tmp_buf->tid = get_stack_tid();
        ret = memcpy_s(&tmp_buf->qtuple, sizeof(*qtuple), qtuple, sizeof(struct gazelle_quintuple));
        if (ret != EOK) {
            LSTACK_LOG(ERR, LSTACK, "memcpy_s failed ret=%d.\n", ret);
            return -1;
        }

        free_buf[0] = tmp_buf;
        sent_pkts = gazelle_ring_sp_enqueue(stack->reg_ring, free_buf, 1);
    } while ((sent_pkts < 1) && (ENQUEUE_RING_RETRY_TIMEOUT > sys_now() - tbegin) && get_register_state());

    return (int32_t)sent_pkts;
}

void vdev_ltran_rule_ops_init(void)
{
    g_vdev_ltran_rule_ops.reg_listen_port = vdev_ltran_reg_xmit;
    g_vdev_ltran_rule_ops.unreg_listen_port = vdev_ltran_reg_xmit;
    g_vdev_ltran_rule_ops.reg_conn = vdev_ltran_reg_xmit;
    g_vdev_ltran_rule_ops.unreg_conn = vdev_ltran_reg_xmit;

    vdev_rule_register(&g_vdev_ltran_rule_ops);
}
